using Circus.CodeEditor;
using Circus.CodeEditor.Serialization;
using Circus.CodeEditor.TextSource;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
namespace Circus.CodeEditor {
    public class Scrolling {
        public RichTextBoxScrollBars scrollBars = RichTextBoxScrollBars.Both;
        public int defaultHorzScrollSize = EditConsts.DefaultHorzScrollSize;
        public int windowOriginX;
        public int windowOriginY;
        public int scrollUpdateCount;
        public ScrollingOptions options = EditConsts.DefaultScrollingOptions;

        public ScrollBar hScrollBar;
        public ScrollBar vScrollBar;
        public int updateCount;
        public CodeEditor owner;

        [Browsable(false)]
        public event EventHandler VerticalScroll;

        [Browsable(false)]
        public event EventHandler HorizontalScroll;

        [DefaultValue(RichTextBoxScrollBars.Both), Description("Gets or sets the type of scroll bars displayed in the control.")]
        public virtual RichTextBoxScrollBars ScrollBars {
            get {
                return this.scrollBars;
            }
            set {
                if (this.scrollBars != value) {
                    this.scrollBars = value;
                    this.UpdateScroll(true);
                }
            }
        }

        [Description("Gets or sets a default size of horizontal scrollbar.")]
        public virtual int DefaultHorzScrollSize {
            get {
                return this.defaultHorzScrollSize;
            }
            set {
                if (this.defaultHorzScrollSize != value) {
                    this.defaultHorzScrollSize = value;
                    this.UpdateScroll(false);
                }
            }
        }
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public virtual int WindowOriginX {
            get {
                return this.windowOriginX;
            }
            set {
                value = Math.Max(value, 0);
                if (this.owner != null) {
                    if (this.ScrollByPixels) {
                        int num = this.owner.Width - this.owner.ClientRect.Width;
                        if (value >= num && num >= 0) {
                            value = num;
                        }
                    }
                }
                if (this.windowOriginX != value) {
                    int ch = this.windowOriginX;
                    this.windowOriginX = value;
                    this.UpdateScrollPosition();
                    this.WindowOriginChanged(this.windowOriginY, ch, false);
                    this.OnHorizontalScroll();
                }
            }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public virtual int WindowOriginY {
            get {
                return this.windowOriginY;
            }
            set {
                if (this.updateCount > 0) {
                    this.windowOriginY = value;
                } else {
                    value = Math.Max(value, 0);
                    if (this.owner != null) {
                        if ((NavigateOptions.BeyondEof & this.owner.NavigateOptions) == NavigateOptions.None) {
                            int num2;
                            if (this.ScrollByPixels) {
                                num2 = this.owner.DisplayLines.DisplayCount * this.owner.Painter.lineHeight - this.owner.ClientHeight + 1;
                            } else {
                                num2 = this.owner.DisplayLines.DisplayCount - this.owner.LinesInHeight + 1;
                            }
                            if (value >= num2) {
                                value = Math.Max(num2, 0);
                            }
                        }
                        if (this.windowOriginY != value) {
                            int num3 = this.windowOriginY;
                            this.windowOriginY = value;
                            this.UpdateScrollPosition();
                            {
                                this.WindowOriginChanged(num3, this.windowOriginX, false);
                            }
                            this.OnVerticalScroll();
                        }
                    }
                }
            }
        }


        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public virtual bool FixedScrollSize {
            get {
                return this.owner.WordWrap || (this.ScrollBars != RichTextBoxScrollBars.Both && this.ScrollBars != RichTextBoxScrollBars.Horizontal && ((this.ScrollBars != RichTextBoxScrollBars.ForcedBoth && this.ScrollBars != RichTextBoxScrollBars.ForcedHorizontal) || this.DefaultHorzScrollSize > 0));
            }
        }

        [Description("Gets or sets a \"ScrollingOptions\" that determine scrolling behaviour."), Editor("QWhale.Design.FlagEnumerationEditor, QWhale.Design", typeof(UITypeEditor))]
        public virtual ScrollingOptions Options {
            get {
                return this.options;
            }
            set {
                if (this.options != value) {
                    this.options = value;
                    this.UpdateScroll(true);
                }
            }
        }
       
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public virtual bool ScrollByPixels {
            get {
                return (this.options & ScrollingOptions.ScrollByPixels) != ScrollingOptions.None;
            }
        }


        private void SetScrollPos(bool vert, int value) {

            ScrollBar scrollBar = vert ? this.vScrollBar : this.hScrollBar;
            if (scrollBar != null) {
                scrollBar.Value = Math.Min(value, scrollBar.Maximum);
            }
        }
        private bool InvalidateLines(int ch, int line) {
            bool result = false;
            if (this.owner.IsHandleCreated && (line != this.WindowOriginY || ch != this.WindowOriginX)) {
                if ((ch != this.WindowOriginX && line != this.WindowOriginY) || this.owner.Selection.SelectionType != SelectionType.None || this.owner.Transparent || this.owner.SyntaxPaint.NeedPaint || this.owner.LineSeparator.NeedHighlight()) {
                    this.owner.Invalidate();
                } else {
                    int num = 0;
                    int num2 = 0;
                    Rectangle clientRect = this.owner.ClientRect;
                    int width = clientRect.Width;
                    int height = clientRect.Height;
                    if (ch == this.WindowOriginX) {
                        if (this.ScrollByPixels) {
                            num2 = line - this.WindowOriginY;
                        } else {
                            num2 = (line - this.WindowOriginY) * this.owner.Painter.lineHeight;
                        }
                    } else {
                        if (line == this.WindowOriginY) {
                            if (this.ScrollByPixels) {
                                num = ch - this.WindowOriginX;
                            } else {
                                num = (ch - this.WindowOriginX) * this.owner.Painter.FontWidth;
                            }
                        }
                    }
                    if (Math.Abs(num) < width / 2 && Math.Abs(num2) < height / 2) {
                        int num3 = clientRect.Left + (this.owner.Gutter.DisplayWidth);
                        Rectangle rect;
                        Rectangle rect2;
                        if (num2 == 0) {
                            if (num > 0) {
                                rect = new Rectangle(num3, clientRect.Top, width - num, height);
                                rect2 = new Rectangle(num3, clientRect.Top, num, height);
                            } else {
                                rect = new Rectangle(num3 - num, clientRect.Top, width + num, height);
                                rect2 = new Rectangle(width + num, clientRect.Top, -num, height);
                            }
                        } else {
                            if (num2 > 0) {
                                rect = new Rectangle(clientRect.Left, clientRect.Top, width, height - num2);
                                rect2 = new Rectangle(clientRect.Left, clientRect.Top, width, num2 - clientRect.Top);
                            } else {
                                rect = new Rectangle(clientRect.Left, clientRect.Top - num2, width, height + num2);
                                rect2 = new Rectangle(clientRect.Left, height + num2, width, -num2);
                            }
                        }
                        OSUtils.ScrollWindow(this.owner.Handle, num, num2, rect);
                        this.owner.Invalidate(rect2);
                        this.owner.SyntaxPaint.NeedPaint = true;
                        result = true;
                    } else {
                        this.owner.Invalidate();
                    }
                }
            }
            return result;
        }
   
        protected void UpdateScrollSize(bool updateSize) {
            bool vert;
            bool horz;
            bool forced;
            this.GetScrollCodes(out vert, out horz, out forced);
            bool hasVScrollBar = this.owner.hasVScrollBar;
            bool hasHScrollBar = this.owner.hasHScrollBar;

            if (vert) {
                int num = this.owner.scrollHeight;
                int num2 = this.ScrollByPixels ? this.owner.ClientRect.Height : this.owner.LinesInHeight;
                if (!forced && num <= num2) {
                    num2 = -1;
                }
                this.SetScrollBar(num, num2, true);
            } else {
                this.SetScrollBar(0, -1, true);
            }

            if (horz) {
                int num = this.owner.scrollWidth;
                int num2 = this.owner.CharsInWidth;
                if (!forced && num <= num2) {
                    num2 = -1;
                }
                this.SetScrollBar(num, num2, false);
            } else {
                this.SetScrollBar(0, -1, false);
            }

            if (updateSize || hasVScrollBar != this.owner.hasVScrollBar || hasHScrollBar != this.owner.hasHScrollBar) {
                Rectangle rectangle = this.owner.ClientRectangle;
                if (this.owner.hasVScrollBar) {
                    rectangle.Width -= this.owner.vScrollBar.Width;
                }
                if (this.owner.hasHScrollBar) {
                    rectangle.Height -= this.owner.hScrollBar.Height;
                }
                Rectangle rectangle2 = rectangle;
                if (this.owner.hasVScrollBar) {
                    this.owner.vScrollBar.Bounds = new Rectangle(rectangle2.Right, rectangle.Top, this.owner.vScrollBar.Width, rectangle.Height);

                    Console.WriteLine("his  {0}", this.owner.vScrollBar.Bounds);

                }
                rectangle = rectangle2;
                if (this.owner.hasHScrollBar) {
                    this.owner.hScrollBar.Bounds = new Rectangle(rectangle.Left, rectangle2.Bottom, rectangle.Width, this.owner.hScrollBar.Height);
                }
            }
        }

        private void SetScrollBar(int size, int pageSize, bool vert) {

            ScrollBar scrollBar = vert ? this.vScrollBar : this.hScrollBar;
            if (pageSize < 0) {
                if (scrollBar != null) {
                    scrollBar.Visible = false;
                    if (vert) {
                        scrollBar.Height = 0;
                    } else {
                        scrollBar.Width = 0;
                    }
                }
            } else {
                scrollBar = (vert ? this.owner.vScrollBar : (ScrollBar)this.owner.hScrollBar);
                scrollBar.Visible = true;
                scrollBar.Maximum = size;
                scrollBar.SmallChange = 1;
                scrollBar.LargeChange = pageSize;
                scrollBar.Enabled = (size >= pageSize);
            }
        }

        protected void UpdateScrollPosition() {
            if (this.owner != null && this.owner.IsHandleCreated) {
                this.scrollUpdateCount++;
                try {
                    bool flag;
                    bool flag2;
                    bool flag3;
                    this.GetScrollCodes(out flag, out flag2, out flag3);
                    if (flag) {
                        this.SetScrollPos(true, this.windowOriginY);
                    }
                    if (flag2) {
                        this.SetScrollPos(false, this.windowOriginX);
                    }
                } finally {
                    this.scrollUpdateCount--;
                }
            }
        }


        private void GetScrollCodes(out bool vert, out bool horz, out bool forced) {
            vert = false;
            horz = false;
            forced = false;
            RichTextBoxScrollBars richTextBoxScrollBars = this.scrollBars;
            switch (richTextBoxScrollBars) {
                case RichTextBoxScrollBars.Horizontal:
                    horz = true;
                    break;
                case RichTextBoxScrollBars.Vertical:
                    vert = true;
                    break;
                case RichTextBoxScrollBars.Both:
                    vert = true;
                    horz = true;
                    break;
                default:
                    switch (richTextBoxScrollBars) {
                        case RichTextBoxScrollBars.ForcedHorizontal:
                            horz = true;
                            forced = true;
                            break;
                        case RichTextBoxScrollBars.ForcedVertical:
                            vert = true;
                            forced = true;
                            break;
                        case RichTextBoxScrollBars.ForcedBoth:
                            horz = true;
                            vert = true;
                            forced = true;
                            break;
                    }
                    break;
            }
        }

        protected void OnVerticalScroll() {
            if (this.VerticalScroll != null) {
                this.VerticalScroll(this.owner, EventArgs.Empty);
            }
        }

        protected void OnHorizontalScroll() {
            if (this.HorizontalScroll != null) {
                this.HorizontalScroll(this.owner, EventArgs.Empty);
            }
        }

        protected virtual void WindowOriginChanged(int line, int ch, bool invalidateOnly) {
            bool flag = this.InvalidateLines(ch, line);
            if (invalidateOnly) {
                if (flag) {
                    this.owner.Update();
                }
            } else {
                this.owner.UpdateCaret();
                if (line != this.WindowOriginY) {
                    if (this.owner.Gutter.InvalidateLineNumberArea(false)) {
                        this.owner.Invalidate();
                        flag = false;
                    }
                }
                if (ch != this.WindowOriginX) {
                    this.owner.OnStateChanged(this, NotifyState.ScrollingOriginChanged);
                }
                if (flag) {
                    this.owner.Update();
                }
            }
        }

        public Scrolling(CodeEditor owner) {
            this.owner = owner;
        }
      
        public virtual int DisableUpdate() {
            this.updateCount++;
            return this.updateCount;
        }
        public virtual int EnableUpdate() {
            this.updateCount--;
            return this.updateCount;
        }

        public virtual void UpdateScroll() {
            this.UpdateScroll(false);
        }
        public virtual void UpdateScroll(bool updateSize) {
            if (this.updateCount <= 0) {
               this.WindowOriginY = this.windowOriginY;
               this.WindowOriginX = this.windowOriginX;
               this.UpdateScrollSize(updateSize);
               this.UpdateScrollPosition();
               if (this.owner != null) {
                   this.owner.OnStateChanged(this, NotifyState.ScrollingOptionsChanged);
               }
            }
        }
    }
}
